package com.liuning.graduationdemo.controller;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.lang.TypeReference;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.SecureUtil;
import cn.hutool.crypto.digest.MD5;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.liuning.graduationdemo.common.Constants;
import com.liuning.graduationdemo.common.Result;
import com.liuning.graduationdemo.entity.Files;
import com.liuning.graduationdemo.entity.User;
import com.liuning.graduationdemo.mapper.FilesMapper;
import com.liuning.graduationdemo.service.IFilesService;
import com.liuning.graduationdemo.service.IUserService;
import org.apache.logging.log4j.util.Strings;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.net.URLEncoder;
import java.util.List;

/**
 * 文件上传相关接口
 */
@RestController
@RequestMapping("/file")
public class FileController {

    @Value("${files.upload.path}")
    private String fileUploadPath;

    @Resource
    private FilesMapper filesMapper;

    @Resource
    private StringRedisTemplate stringRedisTemplate;


    /**
     * 文件上传
     * @param file
     * @return
     * @throws IOException
     */
    @PostMapping("/upload")
    public String upload(@RequestParam MultipartFile file) throws IOException {
        String originalFilename = file.getOriginalFilename();
        String type = FileUtil.extName(originalFilename);
        long size = file.getSize();
        //先存储到磁盘
//        File uploadParentFile = new File(fileUploadPath);

        //定义一个文件唯一标识码
        String uuid = IdUtil.fastSimpleUUID();
        System.out.println("uuid:"+uuid);
        String fileUUID = uuid + StrUtil.DOT + type;
        File uploadFile = new File(fileUploadPath + fileUUID);

        File parentFile = uploadFile.getParentFile();
        //先判断配置的文件目录是否存在，若不存在则创建一个新的目录
        if (!parentFile.exists()) {
            parentFile.mkdirs();
        }

        String md5;
        String url;
        //把获取到的文件存储到磁盘目录
        file.transferTo(uploadFile);
        md5 = SecureUtil.md5(uploadFile);
        //查询文件的md5是否存在(从数据库查询是否存在相同记录)
        Files dbFiles = getFileByMd5(md5);
        //获取文件的url
        if (dbFiles != null) {
            url = dbFiles.getUrl();

            //由于文件已经存在，所以删除刚才上传的重复文件
            uploadFile.delete();
        }else {
            //数据库中如不存在重复文件，则不删除刚才上传的文件
            url = "http://localhost:8085/file/" + fileUUID;
        }

        //存储到数据库
        Files saveFile = new Files();
        saveFile.setName(originalFilename);
        saveFile.setType(type);
        saveFile.setSize(size/1024);
        saveFile.setUrl(url);
        saveFile.setMd5(md5);
        filesMapper.insert(saveFile);

        //先从redis中查出缓存(不用从数据库中查询)
        String json = stringRedisTemplate.opsForValue().get(Constants.FILES_KEY);
        //解析缓存
        List<Files> files = JSONUtil.toBean(json, new TypeReference<List<Files>>() {
        }, true);
        //再将上传的文件添加到缓存中
        files.add(saveFile);
        setCache(Constants.FILES_KEY,JSONUtil.toJsonStr(files));
        return url;
    }

    /**
     * 公告图片上传
     * @param file
     * @return
     * @throws IOException
     */
    @PostMapping("/uploadImg")
    public Object uploadImg(@RequestParam MultipartFile file) throws IOException {
        String originalFilename = file.getOriginalFilename();
        String type = FileUtil.extName(originalFilename);
        long size = file.getSize();
        //先存储到磁盘
//        File uploadParentFile = new File(fileUploadPath);

        //定义一个文件唯一标识码
        String uuid = IdUtil.fastSimpleUUID();
        System.out.println("uuid:"+uuid);
        String fileUUID = uuid + StrUtil.DOT + type;
        File uploadFile = new File(fileUploadPath + fileUUID);

        File parentFile = uploadFile.getParentFile();
        //先判断配置的文件目录是否存在，若不存在则创建一个新的目录
        if (!parentFile.exists()) {
            parentFile.mkdirs();
        }

        String md5;
        String url;
        //把获取到的文件存储到磁盘目录
        file.transferTo(uploadFile);
        md5 = SecureUtil.md5(uploadFile);
        //查询文件的md5是否存在(从数据库查询是否存在相同记录)
        Files dbFiles = getFileByMd5(md5);
        //获取文件的url
        if (dbFiles != null) {
            url = dbFiles.getUrl();

            //由于文件已经存在，所以删除刚才上传的重复文件
            uploadFile.delete();
        }else {
            //数据库中如不存在重复文件，则不删除刚才上传的文件
            url = "http://localhost:8085/file/" + fileUUID;
        }

        //存储到数据库
        Files saveFile = new Files();
        saveFile.setName(originalFilename);
        saveFile.setType(type);
        saveFile.setSize(size/1024);
        saveFile.setUrl(url);
        saveFile.setMd5(md5);
        filesMapper.insert(saveFile);

        //先从redis中查出缓存(不用从数据库中查询)
        String json = stringRedisTemplate.opsForValue().get(Constants.FILES_KEY);
        //解析缓存
        List<Files> files = JSONUtil.toBean(json, new TypeReference<List<Files>>() {
        }, true);
        //再将上传的文件添加到缓存中
        files.add(saveFile);
        setCache(Constants.FILES_KEY,JSONUtil.toJsonStr(files));
        /** 富文本上传图片的返回格式
         * {
         *     // errno 即错误代码，0 表示没有错误。
         *     "errno": 0,
         *     "data": [
         *         {
         *             url: "图片地址",
         *             alt: "图片文字说明",
         *             href: "跳转链接"
         *         }
         *     ]
         * }
         */
        return JSONUtil.parseObj("{\"errno\":0, \"data\":[{\"url\": \"" + url + "\"}]}");
    }

    /**
     * 文件下载路径   http://localhost:8085/file/{fileUUID}
     * @param fileUUID
     * @param response
     * @throws IOException
     */
    @GetMapping("/{fileUUID}")
    public void download(@PathVariable(name = "fileUUID") String fileUUID, HttpServletResponse response) throws IOException {
        //根据文件的唯一标识码获取文件
        File uploadFile = new File(fileUploadPath + fileUUID);
        //设置输出流的格式
        ServletOutputStream os = response.getOutputStream();
        response.setHeader("Content-Disposition","attachment:filename=" + URLEncoder.encode(fileUUID,"UTF-8"));
        response.setContentType("application/octet-stream");

        //读取文件的字节流
        os.write(FileUtil.readBytes(uploadFile));
        os.flush();
        os.close();
    }

    @GetMapping("/detail/{id}")
    public Result getById(@PathVariable(name = "id") Integer id) {
        Files files = filesMapper.selectById(id);
        return Result.success(files);
    }

    /**
     * 分页查询接口
     * @param pageNum
     * @param pageSize
     * @param name
     * @return
     */
    @GetMapping("/page")
    public Result findPage(@RequestParam Integer pageNum,
                           @RequestParam Integer pageSize,
                           @RequestParam(defaultValue = "") String name) {
        QueryWrapper<Files> queryWrapper = new QueryWrapper<>();
        //查询未删除的记录
        queryWrapper.eq("is_delete",false);
        queryWrapper.like(Strings.isNotEmpty(name),"name",name);
        queryWrapper.orderByDesc("id");
        return Result.success(filesMapper.selectPage(new Page<>(pageNum, pageSize),queryWrapper));
    }

    @GetMapping("/front/all")
    public Result frontAll() {
        return Result.success(filesMapper.selectList(null));
    }


    /**
     * 更新enable字段
     * @param files
     * @return
     */
//    @CachePut(value = "files",key = "'frontName'")
    @PostMapping("/update")
    public Result save(@RequestBody Files files) {
        filesMapper.updateById(files);
        flushRedis(Constants.FILES_KEY);
        return Result.success();
    }

    /**
     * 单条删除
     * @param id
     * @return
     */
    @DeleteMapping("/{id}")
//    @CacheEvict(value = "files",key = "'frontName'")
    public Result delete(@PathVariable Integer id) {
        Files files = filesMapper.selectById(id);
        files.setIsDelete(true);
        filesMapper.updateById(files);
        flushRedis(Constants.FILES_KEY);
        return Result.success();
    }

    /**
     * 批量删除
     * @param ids
     * @return
     */
    @PostMapping("/del/batch")
    public Result batchDelete(@RequestBody List<Integer> ids) {
        QueryWrapper<Files> queryWrapper = new QueryWrapper<>();
        queryWrapper.in("id",ids);
        List<Files> files = filesMapper.selectList(queryWrapper);
        for (Files file : files) {
            file.setIsDelete(true);
            filesMapper.updateById(file);
        }
        flushRedis(Constants.FILES_KEY);
        return Result.success();
    }

    /**
     * 通过md5查询文件
     * @param md5
     * @return
     */
    public Files getFileByMd5(String md5) {
        QueryWrapper<Files> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("md5",md5);
        List<Files> filesList = filesMapper.selectList(queryWrapper);
        return filesList.size() == 0 ? null : filesList.get(0);
    }

    /**
     * 设置缓存
     * @param key
     * @param value
     */
    private void setCache(String key,String value) {
        stringRedisTemplate.opsForValue().set(key,value);
    }

    /**
     * 删除redis中的缓存
     * @param key
     */
    private void flushRedis(String key) {
        stringRedisTemplate.delete(key);
    }
}
